package com.zhangzhan.base.utils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 
 * @author HeJun
 * @date 2020年9月28日
 * @Version V1.4
 * @Description JavaBean工具类，通过反射结果缓存机制，可以实现source类对象与target类对象间，同名字段值的高效率、线程安全复制
 * 
 */
@SuppressWarnings("unchecked")
public class HjBeanUtil {
	// 构造器对象的key值
	private static final String CONSTRUCT_KEY = "CONSTRUCT_KEY";
	// 字段对象list的key值
	private static final String FIELDS_LIST_KEY = "FIELDS_LIST_KEY";
	// 创建反射元素缓存池单例对象，饿汉式
	private volatile static CachePool CACHE_POOL = new CachePool();

	/**
	 * @param <T>
	 * @param source
	 * @param targetClazz
	 * @return
	 * @throws Exception
	 * @author HeJun
	 * @date 2020年9月28日
	 * @Version V1.3
	 * @Description 将source对象的同名字段值复制给targetClazz类的实例并返回
	 */
	public static <T> T copy(Object source, Class<T> targetClazz, String... ignoreFields) {
		T target = null;
		// 获取source的类对象
		if (source != null) {
			Class<?> sourceClazz = source.getClass();
			try {
				// 如果缓存池中没有source的缓存就添加，并返回source缓存
				ClazzCache sourceCache = getCacheFromPool(sourceClazz);
				// 实例化target类对象
				target = getInstance(targetClazz);
				// 获得target类的字段List
				List<Field> targetFieldsList = getFieldsList(targetClazz);
				// 遍历targetFieldsList中所有的字段
				for (Field targetField : targetFieldsList) {
					if (ignoreFieldsInvoke(targetField, ignoreFields)) {
						continue;
					}
					// 通过target字段的名字，从sourceCache缓存中获得source的字段（target字段需为source字段的子集）
					Field sourceField = (Field) sourceCache.get(targetField.getName());
					if (sourceField != null) {
						Object value = sourceField.get(source);
						targetField.set(target, value);
					}
				}
			} catch (Exception e) {
				throw new RuntimeException(
						sourceClazz.getName() + " 转 " + targetClazz.getName() + " 失败\n" + e.getMessage(), e);
			}

		}

		return target;

	}

	/**
	 * @param <T>
	 * @param source
	 * @param targetClazz
	 * @return
	 * @throws Exception
	 * @author HeJun
	 * @date 2020年9月28日
	 * @Version V1.3
	 * @Description 将source对象的同名字段值复制给targetClazz类的实例并返回
	 */
	public static <T> T copy(Object source, Class<T> targetClazz) {
		return copy(source, targetClazz, null);
	}

	/**
	 * @param source
	 * @param target
	 * @author HeJun
	 * @date 2020年9月28日
	 * @Version V1.0
	 * @Description 将source对象的同名字段值复制给target对象
	 */
	public static void copy(Object source, Object target, String... ignoreFields) {
		if (source != null) {
			// 获取类对象
			Class<?> sourceClazz = source.getClass();
			Class<?> targetClazz = target.getClass();
			try {
				// 如果缓存池中没有source的缓存就添加，并返回source缓存
				ClazzCache sourceCache = getCacheFromPool(sourceClazz);
				// 获得target类的字段List
				List<Field> targetFieldsList = getFieldsList(targetClazz);
				// 遍历targetFieldsList中所有的字段
				for (Field targetField : targetFieldsList) {
					if (ignoreFieldsInvoke(targetField, ignoreFields)) {
						continue;
					}
					// 通过target字段的名字，从sourceCache缓存中获得source的字段（target字段需为source字段的子集）				
					Field sourceField = (Field) sourceCache.get(targetField.getName());
					if (sourceField != null) {
						Object value = sourceField.get(source);
						targetField.set(target, value);
					}
				}
			} catch (Exception e) {
				throw new RuntimeException(
						sourceClazz.getName() + " 转 " + targetClazz.getName() + " 失败\n" + e.getMessage(), e);
			}
		}
	}

	/**
	 * @param source
	 * @param target
	 * @author HeJun
	 * @date 2020年9月28日
	 * @Version V1.0
	 * @Description 将source对象的同名字段值复制给target对象
	 */
	public static void copy(Object source, Object target) {
		copy(source, target, null);
	}

	private static boolean ignoreFieldsInvoke(Field targetField, String... ignoreFields) {
		boolean flag = false;
		if (ignoreFields != null && ignoreFields.length > 0) {
			String name = targetField.getName();
			for (int i = 0; i < ignoreFields.length; i++) {
				if (name.equals(ignoreFields[i])) {
					flag = true;
					break;
				}
			}
		}
		return flag;
	}

	/**
	 * @param <T>
	 * @param sourceList
	 * @param targetClazz
	 * @return
	 * @author HeJun
	 * @date 2021年4月17日
	 * @Version V1.0
	 * @Description 将sourceList内的元素对象的同名字段值复制给targetList
	 */
	public static <T> List<T> copyList(List<?> sourceList, Class<T> targetClazz, String... ignoreFields) {
		List<T> targetList = null;
		if (sourceList != null) {
			targetList = new ArrayList<T>();
			for (Object source : sourceList) {
				targetList.add(copy(source, targetClazz, ignoreFields));
			}
		}
		return targetList;
	}

	/**
	 * @param <T>
	 * @param sourceList
	 * @param targetClazz
	 * @return
	 * @author HeJun
	 * @date 2021年4月17日
	 * @Version V1.0
	 * @Description 将sourceList内的元素对象的同名字段值复制给targetList
	 */
	public static <T> List<T> copyList(List<?> sourceList, Class<T> targetClazz) {
		return copyList(sourceList, targetClazz, null);
	}

	/**
	 * @param <T>
	 * @param sourceMap
	 * @param targetClazz
	 * @return
	 * @author HeJun
	 * @date 2020年9月29日
	 * @Version V1.0
	 * @Description 将map转换成targetClazz类的实例
	 */
	public static <T> T mapToObect(Map<String, ?> sourceMap, Class<T> targetClazz, String... ignoreFields) {
		T target = null;
		if (!HjBeanUtil.isEmpty(sourceMap)) {
			try {
				// 实例化target类对象
				target = getInstance(targetClazz);
				// 获得target类的字段List
				List<Field> targetFieldsList = getFieldsList(targetClazz);
				// 遍历targetFieldsList中所有的字段
				for (Field targetField : targetFieldsList) {
					if (ignoreFieldsInvoke(targetField, ignoreFields)) {
						continue;
					}
					// 通过target字段的名字，从sourceCache缓存中获得source的字段（target字段需为source字段的子集）
					Object value = sourceMap.get(targetField.getName());
					targetField.set(target, value);
				}
			} catch (Exception e) {

				throw new RuntimeException("sourceMap" + " 转 " + targetClazz.getName() + " 失败\n" + e.getMessage(), e);
			}
		}

		return target;

	}

	/**
	 * @param source
	 * @return
	 * @author HeJun
	 * @date 2020年9月29日
	 * @Version V1.0
	 * @Description source对象转有序map
	 */
	public static LinkedHashMap<String, Object> objectToMap(Object source, String... ignoreFields) {
		LinkedHashMap<String, Object> targetMap = null;
		if (source != null) {
			targetMap = new LinkedHashMap<>();
			// 获取类对象
			Class<?> sourceClazz = source.getClass();
			try {
				// 获得source类的字段List
				List<Field> sourceFieldsList = getFieldsList(sourceClazz);
				// 遍历sourceFieldsList中所有的字段
				for (Field sourceField : sourceFieldsList) {
					Object value = sourceField.get(source);
					if (!ignoreFieldsInvoke(sourceField, ignoreFields) && value != null) {
						targetMap.put(sourceField.getName(), value);
					}
				}
			} catch (Exception e) {
				throw new RuntimeException(sourceClazz.getName() + " 转 " + "targetMap" + " 失败\n" + e.getMessage(), e);
			}
		}

		return targetMap;
	}

	/**
	 * @param clazz
	 * @return
	 * @throws Exception
	 * @author HeJun
	 * @date 2020年9月27日
	 * @Version V1.2
	 * @Description 向缓存池添加反射元素缓存
	 */
	private static ClazzCache getCacheFromPool(Class<?> clazz) throws Exception {
		ClazzCache clazzCache = CACHE_POOL.get(clazz);
		// 如果缓存中没有clazz的缓存就添加
		if (clazzCache == null) {
			clazzCache = addClazzCache(clazz);
		}
		return clazzCache;
	}

	/**
	 * @param clazz
	 * @return
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @author HeJun
	 * @date 2020年9月27日
	 * @Version V1.0
	 * @Description 向缓存池添加ClazzCache缓存，并返回ClazzCache对象
	 */
	private static ClazzCache addClazzCache(Class<?> clazz)
			throws NoSuchMethodException, SecurityException {
		ClazzCache clazzCache = CACHE_POOL.get(clazz);
		if (clazzCache == null) {
			clazzCache = new ClazzCache();
			List<Field> fieldsList = new ArrayList<>();
			Set<String> fieldNamesSet = new HashSet<>();
			for (Class<?> currentClazz = clazz; currentClazz != Object.class; currentClazz = currentClazz
					.getSuperclass()) {
				// 获得当前clazz的字段数组
				Field[] fields = currentClazz.getDeclaredFields();
				for (Field field : fields) {
					// 判断是否是final修饰的字段，如果是就不添加进clazzFields
					if (!Modifier.isFinal(field.getModifiers())) {
						field.setAccessible(true);
						// 判断字段名是否重复，如果重复就不添加
						if (fieldNamesSet.add(field.getName())) {
							fieldsList.add(field);
							clazzCache.put(field.getName(), field);
						}
					}
				}
				// 将类字段list放入clazzCache中
				clazzCache.put(FIELDS_LIST_KEY, fieldsList);
				// 将类的构造器放入clazzCache中
				clazzCache.put(CONSTRUCT_KEY, clazz.getDeclaredConstructor());
			}
			// 将clazzCache反射元素缓存放入缓存池中
			clazzCache = CACHE_POOL.putIfAbsent(clazz, clazzCache);
		}

		return clazzCache;
	}

	/**
	 * @param <T>
	 * @param clazz
	 * @return
	 * @throws Exception
	 * @author HeJun
	 * @date 2020年10月20日
	 * @Version V1.0
	 * @Description 根据类对象返回该类实例
	 */
	private static <T> T getInstance(Class<T> clazz) throws Exception {
		ClazzCache cache = getCacheFromPool(clazz);
		Constructor<T> constructor = (Constructor<T>) cache.get(CONSTRUCT_KEY);
		return constructor.newInstance();
	}

	/**
	 * @param clazz
	 * @return
	 * @throws Exception
	 * @author HeJun
	 * @date 2020年10月20日
	 * @Version V1.0
	 * @Description 从缓存池中获取clazz类的Field对象List
	 */
	public static List<Field> getFieldsList(Class<?> clazz) throws Exception {
		// 如果缓存池中没有source的缓存就添加，并返回source缓存
		ClazzCache sourceCache = getCacheFromPool(clazz);
		// 从sourceCache中获得sourceFieldsList
		return (List<Field>) sourceCache.get(FIELDS_LIST_KEY);

	}

	/**
	 * 类字节码对象缓存类
	 *
	 * @author HeJun
	 */
	static class ClazzCache extends ConcurrentHashMap<String, Object> {

		private static final long serialVersionUID = 688480073708628721L;

	}

	/**
	 * 类字节码对象缓存类的缓存池
	 *
	 * @author HeJun
	 */
	static class CachePool extends ConcurrentHashMap<Class<?>, ClazzCache> {

		private static final long serialVersionUID = -6321066791324989547L;

	}

	public static boolean isEmpty(Collection<?> collection) {
		if (collection == null || collection.size() == 0) {
			return true;
		}
		return false;
	}

	/**
	 * @param str
	 * @return
	 * @author HeJun
	 * @date 2020年8月13日
	 * @Version V1.0
	 * @Description str为null或者去掉字符串两端的空格后，与""比较是否为空字符串
	 */
	public static boolean isEmpty(String str) {
		if (str == null || "".equals(str.trim())) {
			return true;
		}
		return false;
	}

	public static boolean isEmpty(Object[] array) {
		if (array == null || array.length == 0) {
			return true;
		}
		return false;
	}

	public static boolean isEmpty(Map<?, ?> map) {
		if (map == null || map.size() == 0) {
			return true;
		}
		return false;
	}
}
